package com.sprite.support.wechat.utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.sprite.support.wechat.utils.model.AccessToken;
import com.sprite.support.wechat.utils.model.JsTicket;
import com.sprite.support.wechat.utils.model.QrCodeTicket;
import com.sprite.support.wechat.utils.model.TemplateMessage;
import com.sprite.support.wechat.utils.model.WechatUserInfo;
import com.sprite.utils.UtilMisc;
import com.sprite.utils.UtilString;
import com.sprite.utils.cache.UtilCache;
import com.sprite.utils.http.RawData;
import com.sprite.utils.http.UtilHttp;

public final class WechatUtils {
	private static Logger logger = LoggerFactory.getLogger(WechatUtils.class);

	private static boolean enableCache = true;

	/**
	 * 是否开启缓存
	 * @param enableCache
	 */
	public static void setEnableCache(boolean enableCache) {
		WechatUtils.enableCache = enableCache;
	}

	/**
	 * key is AppId
	 */
	private final static UtilCache<AccessToken> accessTokenCache = UtilCache.createUtilCache("_accessTokenCache_");

	private static boolean isSuccess(JSONObject response) {
		if(response == null) return false;

		Integer code = response.getInteger("errcode");
		if(code == null || Integer.valueOf(0).equals(code)) {
			return true;
		}

		return false;
	}

	/**
	 * 获取AccessToken
	 * <a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183">详情</a>
	 * @param appid
	 * @param appsecret
	 * @return
	 */
	public static AccessToken getAccessToken(String appid, String appsecret) {
		AccessToken accessToken = null;
		if(enableCache){
			accessToken =accessTokenCache.get(appid);
		}

		if(accessToken == null) {
			accessToken = new AccessToken(appid);
			if(enableCache){
				accessTokenCache.put(appid, accessToken);
			}
		}

		if(!accessToken.isExpired()) {
			return accessToken;
		}

		String url = UtilString.place("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_crauroratial&appid={}&secret={}", appid, appsecret);

		try {
			String content = UtilHttp.getToText(url);

			logger.info("[getAccessToken] response content: {}", content);


			JSONObject jsonObject = JSONObject.parseObject(content);

			if(isSuccess(jsonObject)) {
				accessToken.refresh(jsonObject.getString("access_token"), jsonObject.getLongValue("expires_in") * 1000);
			}
		} catch (Exception e) {
			logger.error("[getAccessToken] error", e);
		}

		return accessToken;
	}

	/**
	 * key is Appid
	 */
	private final static UtilCache<JsTicket> jsTicket = UtilCache.createUtilCache("_JsTicket_");

	/**
	 * 
	 * @param appId
	 * @param accessToken
	 * @return
	 */
	public static JsTicket getJsTicket(String appId, String accessToken) {
		JsTicket tikect = null;

		if(enableCache){
			tikect = jsTicket.get(appId);
		}


		if(tikect == null) {
			tikect = new JsTicket();
			if(enableCache){
				jsTicket.put(appId, tikect);
			}
		}

		if(!tikect.isExpired()) {
			return tikect;
		}

		String url = UtilString.place("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={}&type=jsapi", accessToken);

		try {
			String content = UtilHttp.getToText(url);

			logger.info("[getJsTicket] response content: {}", content);


			JSONObject jsonObject = JSONObject.parseObject(content);

			if(isSuccess(jsonObject)) {
				tikect.refresh(jsonObject.getString("ticket"), jsonObject.getLongValue("expires_in") * 1000);
			}
		} catch (Exception e) {
			logger.error("[getJsTicket] error", e);
		}

		return tikect;
	}

	/**
	 * 	用户基本信息
	 * 	<a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140839">详情- 获取用户基本信息（包括UnionID机制）</a>
	 * @param accessToken
	 * @param openId
	 * @return
	 */
	public static WechatUserInfo getWechatUserInfo(String accessToken, String openId) {
		String url = UtilString.place("https://api.weixin.qq.com/cgi-bin/user/info?access_token={}&openid={}", accessToken, openId);
		try {
			String content = UtilHttp.getToText(url);

			logger.info("[getWechatUserInfo] response content: {}", content);

			JSONObject jsonObject = JSONObject.parseObject(content);

			if(!isSuccess(jsonObject)) {
				return null;
			}

			return populateUserInfo(jsonObject);
		} catch (Exception e) {
			logger.error("[getWechatUserInfo] error", e);
		}
		return null;
	}

	private static WechatUserInfo populateUserInfo(JSONObject jsonObject) {
		WechatUserInfo wechatUserInfo = new WechatUserInfo();
		wechatUserInfo.setCity(jsonObject.getString("city"));
		wechatUserInfo.setCountry(jsonObject.getString("country"));

		int sex = jsonObject.getIntValue("sex");
		if(sex != 0) {
			wechatUserInfo.setGender(sex == 1?"M":"F");
		}

		wechatUserInfo.setHeadimgurl(jsonObject.getString("headimgurl"));
		wechatUserInfo.setNickname(jsonObject.getString("nickname"));
		wechatUserInfo.setLanguage(jsonObject.getString("language"));
		wechatUserInfo.setOpenid(jsonObject.getString("openid"));
		wechatUserInfo.setProvince(jsonObject.getString("province"));
		wechatUserInfo.setQrScene(jsonObject.getString("qr_scene"));
		wechatUserInfo.setQrSceneStr(jsonObject.getString("qr_scene_str"));
		wechatUserInfo.setRemark(jsonObject.getString("remark"));
		wechatUserInfo.setUnionid(jsonObject.getString("unionid"));
		return wechatUserInfo;
	}

	/**
	 * 	用户基本信息
	 * 	<a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140839">批量获取用户基本信息</a>
	 * @param accessToken
	 * @return
	 */
	public static List<WechatUserInfo> getWechatUserInfo(String accessToken, List<String> openIdList) {
		String url = UtilString.place("https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token={}", accessToken);
		try {

			RawData rawData = new RawData();
			rawData.setCharset("utf-8");

			List<Map<String, String>> userList = new LinkedList<>();

			for(String openId : openIdList) {
				userList.add(UtilMisc.toMap("openid", openId));
			}

			rawData.append(JSONObject.toJSONString(UtilMisc.toMap("user_list", userList)));

			String content = UtilHttp.postToText(url, null, null, rawData);

			logger.info("[getWechatUserInfo] response content: {}", content);

			JSONObject jsonObject = JSONObject.parseObject(content);

			if(!isSuccess(jsonObject)) {
				return null;
			}

			JSONArray userlist  = jsonObject.getJSONArray("user_info_list");

			List<WechatUserInfo> list = new ArrayList<>(userlist.size());

			for(Object user : userlist) {
				list.add(populateUserInfo((JSONObject)user));
			}

			return list;
		} catch (Exception e) {
			logger.error("[getWechatUserInfo] error", e);
		}

		return Collections.emptyList();
	}

	/**
	 * 	获取模板消息ID
	 * 	<a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277">获得模板ID</a>
	 * @param accessToken
	 * @param shortId
	 * @return
	 */
	public static String getTemplateId(String accessToken, String shortId) {
		String url = UtilString.place("https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token={}", accessToken);
		try {

			RawData rawData = new RawData();
			rawData.setCharset("utf-8");

			rawData.append(JSONObject.toJSONString(UtilMisc.toMap("template_id_short", shortId)));

			String content = UtilHttp.postToText(url, null, null, rawData);

			logger.info("[getTemplateId] response content: {}", content);

			JSONObject jsonObject = JSONObject.parseObject(content);

			if(!isSuccess(jsonObject)) {
				return null;
			}

			return jsonObject.getString("template_id");
		} catch (Exception e) {
			logger.error("[getTemplateId] error", e);
		}

		return null;

	}

	// key is appId+shortId
	private static UtilCache<String> templateCache = UtilCache.createUtilCache("_templateCache_");

	public static String sendTemplateMessage(String accessToken, TemplateMessage message) {
		return sendTemplateMessage(null, accessToken, message);
	}

	/**
	 *
	 * 发送模板消息
	 * <a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277">发送模板消息</a>
	 * @param accessToken
	 * @param message
	 * @return msgid
	 */
	public static String sendTemplateMessage(String appId, String accessToken, TemplateMessage message) {
		String url = UtilString.place("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={}", accessToken);
		try {

			RawData rawData = new RawData();
			rawData.setCharset("utf-8");

			String templateId = message.getTemplateId();

			if(UtilString.isEmpty(templateId) && !UtilString.isEmpty(message.getShortId())) {
				templateId = templateCache.get(message.getShortId());
				if(templateId == null) {
					templateId = getTemplateId(accessToken, message.getShortId());
				}

				if(templateId != null && appId != null) {
					templateCache.put(appId+message.getShortId(), templateId);
				}
			}

			Object payload = UtilMisc.toMap("touser", message.getTouser(), "template_id", message.getTemplateId(), "url", "miniprogram", message.getMiniprogram() ,"data", message.getData());

			rawData.append(JSONObject.toJSONString(payload));

			String content = UtilHttp.postToText(url, null, null, rawData);

			logger.info("[sendTemplateMessage] response content: {}", content);

			JSONObject jsonObject = JSONObject.parseObject(content);

			if(!isSuccess(jsonObject)) {
				return null;
			}

			return jsonObject.getString("msgid");
		} catch (Exception e) {
			logger.error("[sendTemplateMessage] error", e);
		}

		return null;
	}

	/**
	 * 	删除模板
	 * 	<a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277">删除模板</a>
	 * @param accessToken
	 * @param templateId
	 */
	public static void delTemplate(String accessToken, String templateId) {
		String url = UtilString.place("https://api.weixin.qq.com/cgi-bin/template/del_private_template?access_token={}", accessToken);
		try {

			RawData rawData = new RawData();
			rawData.setCharset("utf-8");

			Object paylout = UtilMisc.toMap("template_id", templateId);

			rawData.append(JSONObject.toJSONString(paylout));

			String content = UtilHttp.postToText(url, null, null, rawData);

			logger.info("[delTemplate] response content: {}", content);

			JSONObject jsonObject = JSONObject.parseObject(content);

			if(!isSuccess(jsonObject)) {
				logger.info("[delTemplate] delTemplate fail");
			}

		} catch (Exception e) {
			logger.error("[delTemplate] error", e);
		}
	}

	/**
	 * 	永久码
	 * @param accessToken
	 * @param sceneStr
	 * @return
	 */
	public static QrCodeTicket getQrCodeTicketPermanent(String accessToken, String sceneStr) {
		String url = UtilString.place("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={}", accessToken);
		try {

			RawData rawData = new RawData();
			rawData.setCharset("utf-8");

			Object payload = UtilMisc.toMap("action_name", "QR_LIMIT_STR_SCENE", "action_info", UtilMisc.toMap("scene", UtilMisc.toMap("scene_str", sceneStr)));

			rawData.append(JSONObject.toJSONString(payload));

			String content = UtilHttp.postToText(url, null, null, rawData);

			logger.info("[getQrCodeTicketPermanent] response content: {}", content);

			JSONObject jsonObject = JSONObject.parseObject(content);

			if(!isSuccess(jsonObject)) {
				return null;
			}

			QrCodeTicket ticket = new QrCodeTicket();
			ticket.setTicket(jsonObject.getString("ticket"));
			ticket.setUrl(jsonObject.getString("url"));
			return ticket;
		} catch (Exception e) {
			logger.error("[getQrCodeTicketPermanent] error", e);
		}

		return null;
	}

	/**
	 * 	永久码
	 * @param accessToken
	 * @param scenceId
	 * @return
	 */
	public static QrCodeTicket getQrCodeTicketPermanent(String accessToken, int scenceId) {
		String url = UtilString.place("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={}", accessToken);
		try {

			RawData rawData = new RawData();
			rawData.setCharset("utf-8");

			Object paylout = UtilMisc.toMap("action_name", "QR_LIMIT_SCENE", "action_info", UtilMisc.toMap("scene", UtilMisc.toMap("scene_id", scenceId)));

			rawData.append(JSONObject.toJSONString(paylout));

			String content = UtilHttp.postToText(url, null, null, rawData);

			logger.info("[getQrCodeTicketPermanent] response content: {}", content);

			JSONObject jsonObject = JSONObject.parseObject(content);

			if(!isSuccess(jsonObject)) {
				return null;
			}

			QrCodeTicket ticket = new QrCodeTicket();
			ticket.setTicket(jsonObject.getString("ticket"));
			ticket.setUrl(jsonObject.getString("url"));
			return ticket;
		} catch (Exception e) {
			logger.error("[getQrCodeTicketPermanent] error", e);
		}

		return null;
	}

	/**
	 * 	临时码
	 * @param accessToken
	 * @param sceneStr
	 * @param expire 有效期 单位ms
	 * @return
	 */
	public static QrCodeTicket getQrCodeTicketTemporary(String accessToken, String sceneStr, Long expire) {
		String url = UtilString.place("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={}", accessToken);
		try {

			RawData rawData = new RawData();
			rawData.setCharset("utf-8");

			Object paylout = UtilMisc.toMap("expire_seconds", expire/1000,"action_name", "QR_STR_SCENE", "action_info", UtilMisc.toMap("scene", UtilMisc.toMap("scene_str", sceneStr)));

			rawData.append(JSONObject.toJSONString(paylout));

			String content = UtilHttp.postToText(url, null, null, rawData);

			logger.info("[getQrCodeTicketPermanent] response content: {}", content);

			JSONObject jsonObject = JSONObject.parseObject(content);

			if(!isSuccess(jsonObject)) {
				return null;
			}

			QrCodeTicket ticket = new QrCodeTicket();
			ticket.setTicket(jsonObject.getString("ticket"));
			ticket.setUrl(jsonObject.getString("url"));
			return ticket;
		} catch (Exception e) {
			logger.error("[getQrCodeTicketPermanent] error", e);
		}

		return null;
	}

	/**
	 * 	临时码
	 * @param accessToken
	 * @param scenceId
	 * @param expire 有效期 单位ms
	 * @return
	 */
	public static QrCodeTicket getQrCodeTicketTemporary(String accessToken, int scenceId, Long expire) {
		String url = UtilString.place("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={}", accessToken);
		try {

			RawData rawData = new RawData();
			rawData.setCharset("utf-8");

			Object paylout = UtilMisc.toMap("expire_seconds", expire/1000, "action_name", "QR_SCENE", "action_info", UtilMisc.toMap("scene", UtilMisc.toMap("scene_id", scenceId)));

			rawData.append(JSONObject.toJSONString(paylout));

			String content = UtilHttp.postToText(url, null, null, rawData);

			logger.info("[getQrCodeTicketPermanent] response content: {}", content);

			JSONObject jsonObject = JSONObject.parseObject(content);

			if(!isSuccess(jsonObject)) {
				return null;
			}

			QrCodeTicket ticket = new QrCodeTicket();
			ticket.setTicket(jsonObject.getString("ticket"));
			ticket.setUrl(jsonObject.getString("url"));
			return ticket;
		} catch (Exception e) {
			logger.error("[getQrCodeTicketPermanent] error", e);
		}

		return null;
	}


	/**
	 * 	长链接转短链接接口
	 * @param accessToken
	 * @param longUrl
	 * @return
	 */
	public static String getShortUrlByLongUrl(String accessToken, String longUrl) {
		String url = UtilString.place("https://api.weixin.qq.com/cgi-bin/shorturl?access_token={}", accessToken);
		try {

			RawData rawData = new RawData();
			rawData.setCharset("utf-8");

			Object paylout = UtilMisc.toMap("action", "long2short", "long_url", longUrl);

			rawData.append(JSONObject.toJSONString(paylout));

			String content = UtilHttp.postToText(url, null, null, rawData);

			logger.info("[getShortUrlByLongUrl] response content: {}", content);

			JSONObject jsonObject = JSONObject.parseObject(content);

			if(!isSuccess(jsonObject)) {
				return null;
			}

			return jsonObject.getString("short_url");
		} catch (Exception e) {
			logger.error("[getShortUrlByLongUrl] error", e);
		}

		return null;
	}


}
